#include "SPEmitterParticle.h"
#include "SPCamera.h"

//--------------------------------------------------------------------------------------
// SPEmitterParticleRenderer
//--------------------------------------------------------------------------------------
namespace SP
{
	class SPEmitterParticleRenderer : public SPD3DObject
	{		
		friend void SPRenderEmitterParticle( SPEmitterParticle* pEmitterParticle );
	public:
		void Init( );

		void DrawParticle( SPEmitterParticle* );
		void AdvanceParticle( SPEmitterParticle* );

		struct CB_GS
		{
			XMMATRIX m_World;
			XMMATRIX m_ViewProj;
			XMMATRIX m_InvView;				
			XMFLOAT4 m_vColour;		
		};

	private:
		SPEmitterParticleRenderer();
		~SPEmitterParticleRenderer();

		HRESULT Create( );
				
		ID3D11InputLayout*				m_pVertexLayout;

		ID3D11VertexShader*			m_pVS;
		ID3D11GeometryShader*		m_pGS;
		ID3D11PixelShader*				m_pPSColour;
		ID3D11PixelShader*				m_pPSTexture;

		ID3D11Buffer*						m_pcbGS;
		ID3D11SamplerState*			m_pSampleState;
	};

	void SPRenderEmitterParticle( SPEmitterParticle* pEmitterParticle )
	{
		static SPEmitterParticleRenderer staticSPEPR;
		staticSPEPR.DrawParticle( pEmitterParticle );
	}

	SPEmitterParticleRenderer::SPEmitterParticleRenderer( )
	{
		Create( );
	}

	SPEmitterParticleRenderer::~SPEmitterParticleRenderer( )
	{

	}

	HRESULT SPEmitterParticleRenderer::Create( )
	{
		HRESULT hr;

		ID3DBlob* pBlobVS = NULL;
		ID3DBlob* pBlobGS = NULL;
		ID3DBlob* pBlobPSC = NULL;
		ID3DBlob* pBlobPST = NULL;

		// Create the shaders
		V_RETURN(  SPGetRenderEnvironment()->CompileShaderFromFile(  L"hlsl\\ParticleDraw.hlsl", NULL, "VSParticleDraw", "vs_5_0", &pBlobVS ) );
		V_RETURN(  SPGetRenderEnvironment()->CompileShaderFromFile(  L"hlsl\\ParticleDraw.hlsl", NULL, "GSParticleDraw", "gs_5_0", &pBlobGS ) );
		
		D3D_SHADER_MACRO defines[] = { { "PARTICLE_TEXTURE", 0 }, { 0 } };
		V_RETURN(  SPGetRenderEnvironment()->CompileShaderFromFile(  L"hlsl\\ParticleDraw.hlsl", defines, "PSParticleDraw", "ps_5_0", &pBlobPST ) );
		V_RETURN(  SPGetRenderEnvironment()->CompileShaderFromFile(  L"hlsl\\ParticleDraw.hlsl", NULL, "PSParticleDraw", "ps_5_0", &pBlobPSC ) );

		V_RETURN(  m_pDevice->CreateVertexShader(  pBlobVS->GetBufferPointer( ), pBlobVS->GetBufferSize( ), NULL, &m_pVS ) );
		V_RETURN(  m_pDevice->CreateGeometryShader(  pBlobGS->GetBufferPointer( ), pBlobGS->GetBufferSize( ), NULL, &m_pGS ) );
		V_RETURN(  m_pDevice->CreatePixelShader(  pBlobPST->GetBufferPointer( ), pBlobPST->GetBufferSize( ), NULL, &m_pPSTexture ) );
		V_RETURN(  m_pDevice->CreatePixelShader(  pBlobPSC->GetBufferPointer( ), pBlobPSC->GetBufferSize( ), NULL, &m_pPSColour ) );

		// Define the input layout
		D3D11_INPUT_ELEMENT_DESC layout[] =
		{
			{"PARTICLEID", 0, DXGI_FORMAT_R32_UINT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0},
		};
		UINT numElements = ARRAYSIZE( layout );
		// Create the input layout
		V_RETURN( m_pDevice->CreateInputLayout( layout, numElements, pBlobVS->GetBufferPointer(),
			pBlobVS->GetBufferSize(), &m_pVertexLayout ) );

		SAFE_RELEASE(  pBlobVS );
		SAFE_RELEASE(  pBlobGS );
		SAFE_RELEASE(  pBlobPSC );
		SAFE_RELEASE(  pBlobPST );

		// Setup constant buffer
		D3D11_BUFFER_DESC Desc;
		Desc.Usage = D3D11_USAGE_DYNAMIC;
		Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
		Desc.MiscFlags = 0;
		Desc.ByteWidth = sizeof(  CB_GS );
		V_RETURN(  m_pDevice->CreateBuffer(  &Desc, NULL, &m_pcbGS ) );

		// CreateSampleState
		D3D11_SAMPLER_DESC SamplerDesc;
		ZeroMemory(  &SamplerDesc, sizeof( SamplerDesc ) );
		SamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
		SamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
		V_RETURN(  m_pDevice->CreateSamplerState( &SamplerDesc, &m_pSampleState ) );

		return S_OK;
	}

	void SPEmitterParticleRenderer::DrawParticle( SPEmitterParticle* pEmitter )
	{
		m_pImmediateContext->VSSetShader(  m_pVS, NULL, 0 );
		m_pImmediateContext->GSSetShader(  m_pGS, NULL, 0 );
		
		// SetParticleTexture
		if ( pEmitter->m_pParticleTexSRV )
		{
			m_pImmediateContext->PSSetShader(  m_pPSTexture, NULL, 0 );
			m_pImmediateContext->PSSetShaderResources(  0, 1, &pEmitter->m_pParticleTexSRV );
		}
		else
		{	
			m_pImmediateContext->PSSetShader(  m_pPSColour, NULL, 0 );
		}

		// Set IA parameters
		m_pImmediateContext->IASetPrimitiveTopology(  D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );

		// Set Resources 
		ID3D11ShaderResourceView* aRViews[ 3 ] = { 
			pEmitter->m_pPositionSRV, 
			pEmitter->m_pSizeSRV,
			pEmitter->m_pColourSRV };
		m_pImmediateContext->VSSetShaderResources(  0, 3, aRViews );

		// Set ConstantBuffer
		SPCamera* pCamera = SPGetCamera();	

		D3D11_MAPPED_SUBRESOURCE MappedResource;
		m_pImmediateContext->Map(  m_pcbGS, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource );
		CB_GS* pCBGS = ( CB_GS* )MappedResource.pData; 
		//pCBGS->m_WorldViewProj = XMMatrixTranspose( pCamera->GetWorldViewProjMtx( ) );
		//pCBGS->m_InvView = XMMatrixTranspose( pCamera->GetViewInv( ) );
		pCBGS->m_ViewProj = pCamera->GetViewProjMtx( );
		pCBGS->m_InvView = pCamera->GetViewInv( );		
		XMStoreFloat4( &pCBGS->m_vColour, pEmitter->GetColourVector() );
		m_pImmediateContext->Unmap(  m_pcbGS, 0 );
		m_pImmediateContext->GSSetConstantBuffers(  0, 1, &m_pcbGS );

		// SetSampleState
		m_pImmediateContext->PSSetSamplers(  0, 1, &m_pSampleState );

		// SetBlendState
		float vBlendFactor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
		m_pImmediateContext->OMSetBlendState(  pEmitter->GetBlendState(), vBlendFactor, 0xFFFFFFFF  );

		// SetDepthStencilState
		m_pImmediateContext->OMSetDepthStencilState(  pEmitter->GetDepthStencilState(), 0 );

		// SetRasterizerState
		m_pImmediateContext->RSSetState( pEmitter->GetRasterizerState() );

		// Set VB
		m_pImmediateContext->IASetInputLayout( m_pVertexLayout );
		UINT Strides[1];
		UINT Offsets[1];
		ID3D11Buffer* pVB[1];
		pVB[0] = pEmitter->m_pVertexBuffer;
		Strides[0] = sizeof( DWORD );
		Offsets[0] = 0;
		m_pImmediateContext->IASetVertexBuffers( 0, 1, pVB, Strides, Offsets );

		// Draw Particle
		UINT count[2];
		UINT startLocation[2];
		UINT drawCount = pEmitter->m_nDrawCount;
		UINT nextIdx = pEmitter->m_nNextParticlesIdx;
		UINT maxCount = pEmitter->m_nMaxAllocationCount;
	
		count[0] = min( drawCount, nextIdx );
		startLocation[0] = nextIdx - count[0];		

		count[1] = ( drawCount < nextIdx ) ? 0 : drawCount - nextIdx;
		startLocation[1] = maxCount - count[1];

		if ( pEmitter->m_bSort )
		{
			m_pImmediateContext->IASetIndexBuffer( pEmitter->m_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );
			m_pImmediateContext->DrawIndexed( count[0], startLocation[0], 0 );
			m_pImmediateContext->DrawIndexed( count[1], startLocation[1], 0 );
		}
		else
		{
			m_pImmediateContext->Draw( count[0], startLocation[0] );					
			m_pImmediateContext->Draw( count[1], startLocation[1] );
		}

		// Init
		ID3D11Buffer* ppBufNULL[1] = { NULL };
		m_pImmediateContext->GSSetConstantBuffers(  0, 1, ppBufNULL );

		m_pImmediateContext->GSSetShader(  NULL, NULL, 0 );
	}

}


///--------------------------------------------------------------------
///--------------------------------------------------------------------
namespace SP
{
	void SPEmitterParticle::SetZero()
	{
		m_fParticleEmitRemain = 0.0f;
		m_nMaxAllocationCount = 0;

		m_pParticleTex = NULL;
		m_pParticleTexSRV = NULL;	
		m_pParticleLife = NULL;

		m_bSort = false;
		m_pParticleDepthArray = NULL;
		m_pIndex = NULL;
		m_pIndexBuffer = NULL;
		m_pVertexBuffer = NULL;

		m_bAbsParticlePosition = true;
		m_bAbsParticleVelocity = true;
		m_bAbsParticleAcceleration = true;

		m_pPosition = NULL;
		m_pPositionBuffer = NULL;
		m_pPositionSRV = NULL;

		m_pSize = NULL;
		m_pSizeBuffer = NULL;
		m_pSizeSRV = NULL;

		m_pColour = NULL;
		m_pColourBuffer = NULL;
		m_pColourSRV = NULL;

		m_pModulePosition = NULL;
		m_pModuleVelocity = NULL;
		m_pModuleAcceleration = NULL;
		m_pModuleSize = NULL;
		m_pModuleColour = NULL;
		m_pModuleAlpha = NULL;
	}

	SPEmitterParticle::SPEmitterParticle()
	{
		SetZero();

		Create( 20.0f, 2.0f );
	}

	SPEmitterParticle::SPEmitterParticle( SPEmitterParticle* archetype )
		: SPEmitter( archetype )
	{	
		SetZero();

		m_bSort = archetype->m_bSort;
		m_bAbsParticleAcceleration = archetype->m_bAbsParticleAcceleration;
		m_bAbsParticlePosition = archetype->m_bAbsParticlePosition;
		m_bAbsParticleVelocity = archetype->m_bAbsParticleVelocity;

		#define Code_Builder( mn, mtn )	\
			if ( archetype->m_pModule##mn )	\
			{														\
			m_pModule##mn = static_cast< SPModuleParticle##mtn*>( archetype->m_pModule##mn->SpawnInstance() );\
			m_pModule##mn->SetEmitterOwner( this );	\
			}
		/*
		if ( archetype->m_pModulePosition )
		{
			m_pModulePosition = static_cast<SPModuleParticleVector*>( archetype->m_pModulePosition->SpawnInstance() );
			m_pModulePosition->SetEmitterOwner( this );
		}
		*/
		Code_Builder( Position, Vector );
		Code_Builder( Velocity, Vector );
		Code_Builder( Acceleration, Vector );
		Code_Builder( Size, Float );
		Code_Builder( Colour, Vector );
		Code_Builder( Alpha, Float );

		#undef Code_Builder		
		
		Create( archetype->m_fSpawnRate, archetype->m_fParticleLifeTime );
	}

	SPEmitterParticle::~SPEmitterParticle()
	{
		if ( m_bCopy )
		{
			SAFE_DELETE( m_pModulePosition );
			SAFE_DELETE( m_pModuleVelocity );
			SAFE_DELETE( m_pModuleAcceleration );
			SAFE_DELETE( m_pModuleAlpha );
			SAFE_DELETE( m_pModuleColour );
			SAFE_DELETE( m_pModuleSize );
		}
		ReleaseParticle();
	}

	void SPEmitterParticle::Create( SPEmitterParticle* archetype )
	{
		SPEmitter::Create( archetype );

		m_bSort = archetype->m_bSort;
		m_bAbsParticleAcceleration = archetype->m_bAbsParticleAcceleration;
		m_bAbsParticlePosition = archetype->m_bAbsParticlePosition;
		m_bAbsParticleVelocity = archetype->m_bAbsParticleVelocity;

		#define Code_Builder( mn, mtn )	\
			if ( archetype->m_pModule##mn )	\
			{														\
			m_pModule##mn = static_cast< SPModuleParticle##mtn*>( archetype->m_pModule##mn->SpawnInstance() );\
			m_pModule##mn->SetEmitterOwner( this );	\
			}
		/*
		if ( archetype->m_pModulePosition )
		{
			m_pModulePosition = static_cast<SPModuleParticleVector*>( archetype->m_pModulePosition->SpawnInstance() );
			m_pModulePosition->SetEmitterOwner( this );
		}
		*/
		Code_Builder( Position, Vector );
		Code_Builder( Velocity, Vector );
		Code_Builder( Acceleration, Vector );
		Code_Builder( Size, Float );
		Code_Builder( Colour, Vector );
		Code_Builder( Alpha, Float );

		#undef Code_Builder		
		
		Create( archetype->m_fSpawnRate, archetype->m_fParticleLifeTime );
	}

	void SPEmitterParticle::Initialize()
	{
		SPEmitter::Initialize();

		if ( m_bProcessSpawnRate )
		{
			m_nDrawCount = 0;	
			m_nNextParticlesIdx = 0;
		}

		for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
		{
			m_pPosition[i] = XMFLOAT3( 0.0f, 0.0f, 0.0f );
			m_pColour[i] = XMFLOAT4( 1.0f, 1.0f, 1.0f, 1.0f );
			m_pSize[i] = 1.0f;
			m_pParticleLife[i] = 0.0f;
		}

		if ( m_pModulePosition )m_pModulePosition->Init();
		if ( m_pModuleVelocity )m_pModuleVelocity->Init();
		if ( m_pModuleAcceleration )m_pModuleAcceleration->Init();
		if ( m_pModuleSize )m_pModuleSize->Init();
		if ( m_pModuleColour )m_pModuleColour->Init();
		if ( m_pModuleAlpha )m_pModuleAlpha->Init();
	}

	void SPEmitterParticle::Render()
	{
		if ( m_nDrawCount > 0 )
			SPRenderEmitterParticle( this );
	}

	void SPEmitterParticle::ClearModule()
	{
		SPEmitter::ClearModule();

		m_pModulePosition = NULL;
		m_pModuleVelocity = NULL;
		m_pModuleAcceleration = NULL;
		m_pModuleSize = NULL;
		m_pModuleColour = NULL;
		m_pModuleAlpha = NULL;
	}

	void QuickDepthSort( DWORD* indices, float* depths, int lo, int hi )
	{
		//  lo is the lower index, hi is the upper index
		//  of the region of array a that is to be sorted
		int i=lo, j=hi;
		float h;
		int index;
		float x=depths[( lo+hi )/2];

		//  partition
		do
		{    
			while ( depths[i] > x ) i++; 
			while ( depths[j] < x ) j--;
			if ( i<=j )
			{
				h=depths[i]; depths[i]=depths[j]; depths[j]=h;
				index = indices[i]; indices[i] = indices[j]; indices[j] = index;
				i++; j--;
			}
		} while ( i<=j );

		//  recursion
		if ( lo<j ) QuickDepthSort( indices, depths, lo, j );
		if ( i<hi ) QuickDepthSort( indices, depths, i, hi );
	}
	
	void SPEmitterParticle::Advance( float fElapsedTime )
	{
		SPEmitter::Advance( fElapsedTime );
		
		XMMATRIX mTransform = GetTransformMatrix();

		if ( m_pModulePosition )m_pModulePosition->Advance( fElapsedTime );
		if ( m_pModuleVelocity )m_pModuleVelocity->Advance( fElapsedTime );
		if ( m_pModuleAcceleration )m_pModuleAcceleration->Advance( fElapsedTime );
		if ( m_pModuleSize )m_pModuleSize->Advance( fElapsedTime );
		if ( m_pModuleColour )m_pModuleColour->Advance( fElapsedTime );
		if ( m_pModuleAlpha )m_pModuleAlpha->Advance( fElapsedTime );

		if ( m_bProcessSpawnRate )
		{				
			// particle count will emit
			m_fParticleEmitRemain += fElapsedTime * m_fSpawnRate;
			FLOAT newCount = floor( m_fParticleEmitRemain );
			m_fParticleEmitRemain -= newCount;

			if ( SPEmitter::IsDead() )
			{
				// kill particle
				if ( m_fParticleLifeTime * m_fEmitterLoops < m_fEmitterDuration || 
					m_fEmitterCurrentTime > ( ( m_fEmitterLoops - 1 ) * m_fEmitterDuration + m_fParticleLifeTime ) )
				{
					m_nDrawCount = max( m_nDrawCount - newCount, 0 ); 
				}
			}
			else
			{
				//	emit new one
				m_nDrawCount = min( m_nDrawCount + newCount, m_nMaxAllocationCount );

				XMVECTOR vOrigin = XMVectorSet( 0.0f, 0.0f, 0.0f, 1.0f );
				if ( m_bAbsParticlePosition )
					vOrigin = XMVector3Transform( vOrigin, mTransform );				
				
				for ( UINT idx = 0; idx < newCount; idx++ )
				{
					UINT particle =  m_nNextParticlesIdx % m_nMaxAllocationCount;
					m_nNextParticlesIdx = particle + 1;
					m_pParticleLife[particle] = 0.0f;
					//EmitParticle( particle );
					if ( m_pModulePosition ) m_pModulePosition->Emit( particle, vOrigin );
					if ( m_pModuleVelocity ) m_pModuleVelocity->Emit( particle );
					if ( m_pModuleAcceleration ) m_pModuleAcceleration->Emit( particle );
					if ( m_pModuleSize ) m_pModuleSize->Emit( particle );
					if ( m_pModuleColour )	m_pModuleColour->Emit( particle );
					if ( m_pModuleAlpha ) m_pModuleAlpha->Emit( particle );
				}
			}
		}


		// Advacne ParticleLife
		if ( m_fParticleLifeTime > 0.0f )
		{
			float deltaLife = fElapsedTime / m_fParticleLifeTime;
			for ( UINT idx = 0; idx < m_nMaxAllocationCount; idx++ )
			{
				m_pParticleLife[idx] += deltaLife;
			}
		}
		
		// Position
		for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
		{
			if ( m_pModuleAcceleration )
			{

				XMVECTOR accel = m_pModuleAcceleration->GetVector( i, m_pParticleLife[i] );
				if ( !m_bAbsParticleAcceleration )
					accel = XMVector3Transform( accel, mTransform );			
				if ( m_pModuleVelocity )
					m_pModuleVelocity->AdvanceVector( i, accel * fElapsedTime );
			}
			if ( m_pModuleVelocity )
			{		
				XMVECTOR velo = m_pModuleVelocity->GetVector( i, m_pParticleLife[i] );			
				if ( !m_bAbsParticleVelocity )
					velo = XMVector3Transform( velo, mTransform );
				if ( m_pModulePosition )
					m_pModulePosition->AdvanceVector( i, velo * fElapsedTime );
			}

			if ( m_pModulePosition )
			{
				XMVECTOR pos = m_pModulePosition->GetVector( i, m_pParticleLife[i] );
				if (!m_bAbsParticlePosition )
					pos = XMVector3Transform( pos, mTransform );
				XMStoreFloat3( m_pPosition+i, pos );
			}
			else
			{
				m_pPosition[i] = XMFLOAT3( 0.0f, 0.0f, 0.0f );
			}
		}

		// Size
		if ( m_pModuleSize )
		{
			for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
			{
				m_pSize[i] = m_pModuleSize->GetFloat( i, m_pParticleLife[i] );
			}
		}

		// Colour
		if ( m_pModuleColour )
		{
			for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
			{
				XMVECTOR v = m_pModuleColour->GetVector( i, m_pParticleLife[i] );
				XMStoreFloat4( m_pColour + i, v );
			}
		}

		if ( m_pModuleAlpha )
		{
			for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
			{
				m_pColour[i].w = m_pModuleAlpha->GetFloat( i, m_pParticleLife[i] );
			}
		}

		// DepthSort
		if ( m_bSort )
		{
			XMVECTOR vEye = XMLoadFloat3( &SPGetCamera()->GetEyePt() );
			XMVECTOR vLookat = XMLoadFloat3( &SPGetCamera()->GetLookatPt() );
			XMVECTOR vDir =  XMVector3Normalize( vLookat - vEye );
			XMVECTOR vToParticle, vDot;
			for( UINT i=0; i<m_nMaxAllocationCount; i++ )
			{
				m_pIndex[i] = i;
				vToParticle = XMLoadFloat3( m_pPosition + i ) - vEye;
				vDot = XMVector3Dot( vDir, vToParticle );
				m_pParticleDepthArray[i] = XMVectorGetX( XMVector3Dot( vDir, vToParticle ) );
			}
			QuickDepthSort( m_pIndex, m_pParticleDepthArray, 0, m_nMaxAllocationCount-1 );
		}

		UpdateParticleBuffer();
	}

	void SPEmitterParticle::UpdateParticleBuffer()
	{
		m_pImmediateContext->UpdateSubresource( m_pPositionBuffer, NULL, NULL, m_pPosition, 0, 0 );
		m_pImmediateContext->UpdateSubresource( m_pSizeBuffer, NULL, NULL, m_pSize, 0, 0 );
		m_pImmediateContext->UpdateSubresource( m_pColourBuffer, NULL, NULL, m_pColour, 0, 0 );
		if ( m_bSort )
			m_pImmediateContext->UpdateSubresource( m_pIndexBuffer, NULL, NULL, m_pIndex, 0, 0 );
	}
	
	void  SPEmitterParticle::CreateFileBuffer( SPFileBuffer* pFB )
	{
		HRESULT hr;
		ID3DBlob* pBlob = NULL;

		SPFileBuffer* pEmitterFB = new SPFileBuffer();
		SPEmitter::CreateFileBuffer( pEmitterFB );

		int numbyte = pEmitterFB->nNumByte;
		numbyte += sizeof( EmitterParticleFileHead );

		// EmitterParticle
		EmitterParticleFileHead head;
		ZeroMemory( &head, sizeof( EmitterParticleFileHead ) );

		head.bSort = m_bSort;
		head.fParticleLifeTime = m_fParticleLifeTime;
		head.fSpawnRate = m_fSpawnRate;
		head.bProcessSpawnRate = m_bProcessSpawnRate;

		if ( m_pModuleColour )
		{
			head.bModuleColour = true;
			head.ModuleColur.InitByModule( m_pModuleColour );
		}

		if ( m_pModulePosition )
		{
			head.bModulePos = true;
			head.ModulePos.InitByModule( m_pModulePosition );
		}

		if ( m_pModuleVelocity )
		{
			head.bModuleVelo = true;
			head.ModuleVelo.InitByModule( m_pModuleVelocity );
		}

		if ( m_pModuleAcceleration )
		{
			head.bModuleAccel = true;
			head.ModuleAccle.InitByModule( m_pModuleAcceleration );
		}

		if ( m_pModuleAlpha )
		{
			head.bMoudleAlpha = true;
			head.ModuleAlpha.InitByModule( m_pModuleAlpha );
		}

		if ( m_pModuleSize )
		{
			head.bModuleSize = true;
			head.ModuleSize.InitByModule( m_pModuleSize );
		}

		head.bTexture = false;		
		if ( m_pParticleTex )
		{
			V( D3DX11SaveTextureToMemory( SPGetD3D11DeviceContext(), m_pParticleTex, D3DX11_IFF_DDS, &pBlob, NULL ) );
			numbyte += pBlob->GetBufferSize();			
			head.bTexture = true;		
			head.nTextureBufferSize = pBlob->GetBufferSize();
		}

		pFB->CreateBuffer( numbyte );
		BYTE* pBuf =  pFB->pFileBuffer;
		// Emitter
		CopyMemory( pBuf, pEmitterFB->pFileBuffer, pEmitterFB->nNumByte );
		pBuf += pEmitterFB->nNumByte;

		CopyMemory( pBuf, &head, sizeof( EmitterParticleFileHead ) );
		pBuf += sizeof( EmitterParticleFileHead );

		if ( head.bTexture )
		{
			CopyMemory( pBuf, pBlob->GetBufferPointer(), pBlob->GetBufferSize() );
			pBuf += pBlob->GetBufferSize();
		}

		SAFE_DELETE( pEmitterFB );
		SAFE_RELEASE( pBlob );
	}

	void SPEmitterParticle::CreateFromFileBuffer( BYTE* pBufPtr )
	{
		HRESULT hr;

		SPEmitter::CreateFromFileBuffer( pBufPtr );

		UINT offset = ( (EmitterFileHead*)pBufPtr )->nByteCount;
		BYTE* pBuf = pBufPtr + offset;
		EmitterParticleFileHead* pHead = (EmitterParticleFileHead*)pBuf;

		m_fSpawnRate = pHead->fSpawnRate;
		m_fParticleLifeTime = pHead->fParticleLifeTime;
		m_bSort = pHead->bSort;
		m_bProcessSpawnRate = pHead->bProcessSpawnRate;

		if ( pHead->bModulePos )
		{
			m_pModulePosition = pHead->ModulePos.ToModule();
			m_pModulePosition->SetEmitterOwner( this );
		}

		if ( pHead->bModuleVelo )
		{
			m_pModuleVelocity = pHead->ModuleVelo.ToModule();
			m_pModuleVelocity->SetEmitterOwner( this );
		}

		if( pHead->bModuleAccel )
		{
			m_pModuleAcceleration = pHead->ModuleAccle.ToModule();
			m_pModuleAcceleration->SetEmitterOwner( this );
		}

		if( pHead->bModuleColour )
		{
			m_pModuleColour = pHead->ModuleColur.ToModule();
			m_pModuleColour->SetEmitterOwner( this );
		}

		if( pHead->bMoudleAlpha )
		{
			m_pModuleAlpha = pHead->ModuleAlpha.ToModule();
			m_pModuleAlpha->SetEmitterOwner( this );
		}

		if( pHead->bModuleSize )
		{
			m_pModuleSize = pHead->ModuleSize.ToModule();
			m_pModuleSize->SetEmitterOwner( this );
		}
	
		Create( m_fSpawnRate, m_fParticleLifeTime );

		if ( pHead->bTexture )
		{
			pBuf += sizeof( EmitterParticleFileHead );
			V( D3DX11CreateTextureFromMemory( SPGetD3D11Device(), pBuf, pHead->nTextureBufferSize, NULL, NULL, &m_pParticleTex, NULL) );
			V( D3DX11CreateShaderResourceViewFromMemory( SPGetD3D11Device(), pBuf, pHead->nTextureBufferSize, NULL, NULL, &m_pParticleTexSRV, NULL) );
		}
	}

	bool SPEmitterParticle::SetSPModule( SPModule* pModule )
	{
		if ( SPModuleInitPos* p = dynamic_cast<SPModuleInitPos*>( pModule ) )
		{			
			m_pModulePosition = p;			
			return true;
		}
		
		if ( SPModulePosOverLife* p = dynamic_cast<SPModulePosOverLife*>( pModule ) )
		{
			m_pModulePosition = p;
			return true;
		}

		if ( SPModuleInitVelo* p = dynamic_cast<SPModuleInitVelo*>( pModule ) )
		{
			m_pModuleVelocity = p;
			return true;
		}

		if ( SPModuleVeloOverLife* p = dynamic_cast<SPModuleVeloOverLife*>( pModule ) )
		{
			m_pModuleVelocity = p;
			return true;
		}

		if ( SPModuleInitAccel* p = dynamic_cast<SPModuleInitAccel*>( pModule ) )
		{
			m_pModuleAcceleration = p ;
			return true;
		}

		if ( SPModuleAccelOverLife* p = dynamic_cast<SPModuleAccelOverLife*>( pModule ) )
		{
			m_pModuleAcceleration = p ;
			return true;
		}

		if ( SPModuleSizeOverLife* p = dynamic_cast<SPModuleSizeOverLife*>( pModule ) )
		{
			m_pModuleSize = p;
			return true;
		}

		if ( SPModuleInitSize* p = dynamic_cast<SPModuleInitSize*>( pModule ) )
		{
			m_pModuleSize = p;
			return true;
		}

		if ( SPModuleColourOverLife* p = dynamic_cast<SPModuleColourOverLife*>( pModule ) )
		{
			m_pModuleColour = p;
			return true;
		}

		if ( SPModuleInitColour* p = dynamic_cast<SPModuleInitColour*>( pModule ) )
		{
			m_pModuleColour = p;
			return true;
		}

		if ( SPModuleAlphaOverLife* p = dynamic_cast<SPModuleAlphaOverLife*>( pModule ) )
		{
			m_pModuleAlpha = p;
			return true;
		}

		if ( SPModuleInitAlpha* p = dynamic_cast<SPModuleInitAlpha*>( pModule ) )
		{
			m_pModuleAlpha = p;
			return true;
		}

		return SPEmitter::SetSPModule( pModule );
	}

	void  SPEmitterParticle::Create( UINT maxParticles )
	{
		m_bProcessSpawnRate = false;
		m_nDrawCount = maxParticles;

		CreateParticle( maxParticles );
	}

	void  SPEmitterParticle::Create( float fSpawnRate, float fParticleLifeTime )
	{
		m_bProcessSpawnRate = true;
		m_nDrawCount = 0;
		m_nNextParticlesIdx = 0;

		m_fSpawnRate = fSpawnRate;
		m_fParticleLifeTime = fParticleLifeTime;
		UINT count = ceil( fSpawnRate * fParticleLifeTime );
		CreateParticle( count );
	}

	HRESULT SPEmitterParticle::CreateParticle( UINT nInitialAllocationCount )
	{
		HRESULT hr = S_OK;

		ReleaseParticle();

		m_nMaxAllocationCount = nInitialAllocationCount;

		m_pParticleLife = new FLOAT[m_nMaxAllocationCount];
		m_pPosition = new XMFLOAT3[m_nMaxAllocationCount];

		m_pSize = new FLOAT[m_nMaxAllocationCount];		
		m_pColour = new XMFLOAT4[m_nMaxAllocationCount];

		if ( m_pModulePosition )m_pModulePosition->Advance( 0 );
		if ( m_pModuleVelocity )m_pModuleVelocity->Advance( 0 );
		if ( m_pModuleAcceleration )m_pModuleAcceleration->Advance( 0 );
		if ( m_pModuleSize )m_pModuleSize->Advance( 0 );
		if ( m_pModuleColour )m_pModuleColour->Advance( 0 );
		if ( m_pModuleAlpha )m_pModuleAlpha->Advance( 0 );

		Initialize();

		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( sizeof( XMFLOAT3 ), m_nMaxAllocationCount, NULL, &m_pPositionBuffer ) );
		V_RETURN( SPGetRenderEnvironment()->CreateBufferSRV( m_pPositionBuffer, &m_pPositionSRV ) );

		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( sizeof( FLOAT ), m_nMaxAllocationCount, NULL, &m_pSizeBuffer ) );
		V_RETURN( SPGetRenderEnvironment()->CreateBufferSRV( m_pSizeBuffer, &m_pSizeSRV ) );

		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( sizeof( XMFLOAT4 ), m_nMaxAllocationCount, NULL, &m_pColourBuffer ) );
		V_RETURN( SPGetRenderEnvironment()->CreateBufferSRV( m_pColourBuffer, &m_pColourSRV ) );

		m_pParticleDepthArray = new FLOAT[m_nMaxAllocationCount];
		m_pIndex = new DWORD[m_nMaxAllocationCount];
		for ( UINT i = 0; i < m_nMaxAllocationCount; i++ )
		{
			m_pIndex[i] = i;
		}
		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( sizeof( DWORD ), m_nMaxAllocationCount, m_pIndex, &m_pIndexBuffer ) );		
		V_RETURN( SPGetRenderEnvironment()->CreateBuffer( sizeof( DWORD ), m_nMaxAllocationCount, m_pIndex, &m_pVertexBuffer ) );

		return hr;
	}

	bool SPEmitterParticle::IsDead()
	{
		return m_fEmitterLoops != 0 && m_fEmitterCurrentTime> (m_fEmitterLoops * m_fEmitterDuration + m_fParticleLifeTime); 	
	}

	void SPEmitterParticle::ReleaseParticle()
	{
		SAFE_DELETE( m_pPosition );

		SAFE_DELETE( m_pSize );
		SAFE_DELETE( m_pColour );

		SAFE_RELEASE( m_pPositionBuffer );
		SAFE_RELEASE( m_pPositionSRV );
		SAFE_RELEASE( m_pSizeBuffer );
		SAFE_RELEASE( m_pSizeSRV );
		SAFE_RELEASE( m_pColourBuffer );
		SAFE_RELEASE( m_pColourSRV );

		SAFE_DELETE( m_pParticleDepthArray );
		SAFE_DELETE( m_pIndex );
		SAFE_RELEASE( m_pIndexBuffer );
		SAFE_RELEASE( m_pVertexBuffer );
	}

	void SPEmitterParticle::EmitParticle( UINT idx )
	{
		if ( m_pModulePosition )			m_pModulePosition->Emit( idx );
		if ( m_pModuleVelocity )			m_pModuleVelocity->Emit( idx );
		if ( m_pModuleAcceleration )	m_pModuleAcceleration->Emit( idx );
		if ( m_pModuleSize )					m_pModuleSize->Emit( idx );
		if ( m_pModuleColour )				m_pModuleColour->Emit( idx );
		if ( m_pModuleAlpha )				m_pModuleAlpha->Emit( idx );
	}

	HRESULT SPEmitterParticle::CreateTextureFromFile( const wchar_t* file )
	{
		HRESULT hr = S_OK;

		V_RETURN( D3DX11CreateTextureFromFile(  m_pDevice, file, NULL, NULL, &m_pParticleTex, NULL ) );
		V_RETURN( D3DX11CreateShaderResourceViewFromFile(  m_pDevice, file, NULL, NULL, &m_pParticleTexSRV, NULL ) );

		return hr;
	}
}

